package com.huahua.api.util;

import com.google.common.collect.Lists;
import com.huahua.api.common.constant.Consant;
import com.huahua.api.entity.developTool.github.dto.GitBranchesDTO;
import com.huahua.api.enums.GitBranchType;
import lombok.extern.slf4j.Slf4j;
import org.dromara.hutool.core.collection.ListUtil;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.SymbolicRef;
import org.eclipse.jgit.transport.*;
import org.eclipse.jgit.transport.RemoteRefUpdate.Status;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * Git工具类
 * 更多jgit的操作，请看：
 * 官方文档：http://archive.eclipse.org/jgit/site/3.7.0.201502260915-r/index.html
 * 示例代码：https://github.com/centic9/jgit-cookbook
 *
 * @author chentiefeng
 * @date 2021/2/10 14:02
 */
@Slf4j
public class GitUtils {

    /**
     * 提交文件到仓库 (包括新增的、更改的、删除的)
     *
     * @param git
     * @param credential
     * @param filePattern
     * @param commitMessage
     * @return
     * @throws GitAPIException
     * @throws IOException
     */
    public static boolean commitFiles(Git git, CredentialsProvider credential, String filePattern, String commitMessage) throws GitAPIException, IOException {
        //添加
        git.add().addFilepattern(filePattern).call();
        git.add().addFilepattern(filePattern).setUpdate(true).call();
        //提交
        git.commit().setMessage(commitMessage).call();
        //推送到远程
        Iterable<PushResult> pushResults = git.push().setCredentialsProvider(credential).call();
        printCommitPushResult(pushResults, git.getRepository().getBranch(), commitMessage);
        return true;
    }

    /**
     * 打印提交文件的日志
     *
     * @param results
     */
    private static void printCommitPushResult(Iterable<PushResult> results, String branchName, String commitMessage) {
        log.info("git add && git commit -m '{}'", commitMessage);
        log.info("git push");
        for (PushResult result : results) {
            RemoteRefUpdate remoteRefUpdate = result.getRemoteUpdate(GitBranchType.LOCAL.getPrefix() + branchName);
            if (Status.OK.equals(remoteRefUpdate.getStatus())) {
                log.info("remote: " + result.getMessages().substring(0, result.getMessages().length() - 1));
            } else {
                log.error("remote: " + result.getMessages());
            }
            log.info("To {}", result.getURI());
        }
    }

    /**
     * 删除分支
     *
     * @param git
     * @param credential
     * @param branchName
     * @return
     * @throws GitAPIException
     * @throws IOException
     */
    public static boolean removeBranch(Git git, CredentialsProvider credential, String branchName) throws GitAPIException, IOException {
        // master分支不能删除
        if ("master".equals(branchName)) {
            return false;
        }

        String oldBranch = git.getRepository().getBranch();
        //如果要删除的分支等于当前分支，切换到master
        if (oldBranch.equals(branchName)) {
            git.checkout().setName("master").call();
        }
        git.branchDelete().setBranchNames(GitBranchType.LOCAL.getPrefix() + branchName).setForce(true).call();
        git.branchDelete().setBranchNames(GitBranchType.REMOTE.getPrefix() + branchName).setForce(true).call();
        //推送到远程
        String branchFullName = GitBranchType.LOCAL.getPrefix() + branchName;
        RefSpec refSpec = new RefSpec(":" + branchFullName).setForceUpdate(true);
        Iterable<PushResult> results = git.push().setCredentialsProvider(credential).setRefSpecs(refSpec).call();
        printRemoveBranchResult(results, branchFullName, branchName);
        return true;
    }

    /**
     * 打印删除分支的日志
     *
     * @param results
     * @param branchFullName
     */
    private static void printRemoveBranchResult(Iterable<PushResult> results, String branchFullName, String branchName) {
        log.info("git push origin --delete {}", branchName);
        for (PushResult result : results) {
            RemoteRefUpdate remoteRefUpdate = result.getRemoteUpdate(branchFullName);
            if (Status.OK.equals(remoteRefUpdate.getStatus())) {
                log.info("remote: " + result.getMessages().substring(0, result.getMessages().length() - 1));
            } else {
                log.error("remote: " + result.getMessages());
            }
            log.info("To {}", result.getURI());
            log.info("- [deleted]    {}", branchName);
        }
    }

    /**
     * 创建分支
     *
     * @param git
     * @param branchName
     * @return
     * @throws GitAPIException
     * @throws IOException
     */
    public static String createBranch(Git git, CredentialsProvider credential, String branchName) throws GitAPIException, IOException {
        //如果本地存在分支,直接返回
        if (getBranches(git, GitBranchType.LOCAL).contains(branchName)) {
            return branchName;
        }
        //如果远端存在分支，则创建本地分支
        if (getBranches(git, GitBranchType.REMOTE).contains(branchName)) {
            String oldBranch = git.getRepository().getBranch();
            git.checkout().setName(branchName).setCreateBranch(true).call();
            git.checkout().setName(oldBranch).call();
            return branchName;
        }
        //新建分支
        git.branchCreate().setName(branchName).call();
        String oldBranch = git.getRepository().getBranch();
        git.checkout().setName(branchName).call();
        //推送到远程
        git.push().setCredentialsProvider(credential).call();
        git.checkout().setName(oldBranch).call();
        return branchName;
    }

    /**
     * 获取所有分支
     *
     * @param git
     * @param branchType 分支类型，分为本地分支和远程分支
     * @return
     * @throws GitAPIException
     * @throws IOException
     */
    public static List<GitBranchesDTO> getBranches(Git git, GitBranchType branchType) throws GitAPIException, IOException {
        if (GitBranchType.LOCAL.equals(branchType)) {
            List<Ref> refs = git.branchList().call();
            ListUtil.reverse(refs);

            String currentBranch = git.getRepository().getBranch();

            ArrayList<GitBranchesDTO> gitBranchesDTOS = Lists.newArrayList();
            for (Ref ref : refs) {
                GitBranchesDTO e = new GitBranchesDTO();
                String branchName = ref.getName();
                e.setBranchName(branchName.substring(GitBranchType.LOCAL.getPrefix().length()));
                e.setObjectId(ref.getObjectId().toString());
                if (currentBranch.equals(branchName)) {
                    e.setDefaultFlag(Consant.YesOrNoConstant.YES);
                }
                gitBranchesDTOS.add(e);
            }
            return gitBranchesDTOS;
        } else {
            List<Ref> refs = git.getRepository().getRefDatabase().getRefs();
            String currentBranch = git.getRepository().getBranch();
//            return refs.stream().filter(item -> !(item instanceof SymbolicRef))
//                    .filter(item -> item.getName().startsWith(GitBranchType.REMOTE.getPrefix()))
//                    .map(ref -> ref.getName().substring(GitBranchType.REMOTE.getPrefix().length()))
//                    .collect(Collectors.toList());

            List<Ref> removeRefList = refs.stream().filter(item -> !(item instanceof SymbolicRef))
                    .filter(item -> item.getName().startsWith(GitBranchType.REMOTE.getPrefix()))
                    .collect(Collectors.toList());
            ListUtil.reverse(removeRefList);
            ArrayList<GitBranchesDTO> data = Lists.newArrayList();
            for (Ref ref : removeRefList) {
                GitBranchesDTO e = new GitBranchesDTO();
                String branchName = ref.getName();
                String branchNameRemoved = branchName.substring(GitBranchType.REMOTE.getPrefix().length());
                e.setBranchName(branchNameRemoved);
                e.setObjectId(ref.getObjectId().toString());
                if (currentBranch.equals(branchNameRemoved)) {
                    e.setDefaultFlag(Consant.YesOrNoConstant.YES);
                } else {
                    e.setDefaultFlag(Consant.YesOrNoConstant.NO);
                }
                data.add(e);
            }
            return data;
        }

    }

    /**
     * 获取git对象
     *
     * @param gitUrl              git的http路径
     * @param credentialsProvider 认证
     * @param localPath           本地路径
     * @return
     * @throws IOException
     * @throws GitAPIException
     */
    public static Git getGit(String gitUrl, CredentialsProvider credentialsProvider, String localPath) throws IOException, GitAPIException {
        if (new File(localPath).exists()) {
            Git open = Git.open(new File(localPath));
            open.pull();
            return open;
        } else {
            return Git.cloneRepository().setCredentialsProvider(credentialsProvider).setURI(gitUrl)
                    .setDirectory(new File(localPath)).call();
        }
    }

    /**
     * 关闭git
     *
     * @param git
     */
    public static void closeGit(Git git) {
        git.close();
    }

    /**
     * 创建Git认证信息
     *
     * @param username
     * @param password
     * @return
     */
    public static CredentialsProvider createCredentialsProvider(String username, String password) {
        return new UsernamePasswordCredentialsProvider(username, password);
    }

}
